package net.jalbum.filters;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;

public class ResizableCircle extends ResizableObject{

	/**
	 * 
	 */
	private static final long serialVersionUID = 4723593839833176721L;
	protected double Radius = 0.1;
	protected double Radius2 = 0.2;
	protected double RadiusOut = 0.1;
	protected double Radius2Out = 0.2;
	protected boolean showRadius2 = false;
	protected static final int RESIZE_NW_2 = 11;
	protected static final int RESIZE_NE_2 = 12;
	protected static final int RESIZE_SE_2 = 13;
	protected static final int RESIZE_SW_2 = 14;
	protected static final int RESIZE_N_2 = 15;
	protected static final int RESIZE_E_2 = 16;
	protected static final int RESIZE_S_2 = 17;
	protected static final int RESIZE_W_2 = 18;
	
	//Variables for settings
	protected int crossSize = 20;

	public ResizableCircle() {
		super();
	}

	public ResizableCircle(int width, int height) {
		super(width, height);
		X1=0.5;
		Y1=0.5;
	}
	
	public ResizableCircle(int width, int height, ResizableObjectPanel parentPanel){
		this(width,height);
		setParentPanel(parentPanel);
	}
	
	public void paint(Graphics g) {
		if(!initial){
			checkDimensions(false);
			g.setColor(color);
			
			//calculate the positions as pixel size
			int intX1 = (int)(OutX1*(width-1));
			int intY1 = (int)(OutY1*(height-1));
			int intRadius1 = (int)(RadiusOut*(width-1));
			int intRadius2 = (int)(Radius2Out*(width-1));
			
			//draw outer circle
			g.drawOval(intX1-intRadius1, intY1-intRadius1, 2*intRadius1, 2*intRadius1);
			if(showRadius2){
				//draw inner circle
				g.drawOval(intX1-intRadius2, intY1-intRadius2, 2*intRadius2, 2*intRadius2);
			}
			//draw center cross
			g.drawLine(intX1, intY1-crossSize/2, intX1, intY1+crossSize/2);
			g.drawLine(intX1-crossSize/2, intY1, intX1+crossSize/2, intY1);
			//draw the handles if the mouse is inside or near the circle
			if(position==MOVE||position==RESIZE_NW||position==RESIZE_NE||position==RESIZE_SE||position==RESIZE_SW||position==RESIZE_N||position==RESIZE_E||position==RESIZE_S||position==RESIZE_W){
				//paint outer handles
				paintHandles(g);
			}
			if(showRadius2 && (position==MOVE||position==RESIZE_NW_2||position==RESIZE_NE_2||position==RESIZE_SE_2||position==RESIZE_SW_2||position==RESIZE_N_2||position==RESIZE_E_2||position==RESIZE_S_2||position==RESIZE_W_2)){
				//Paint inner handles
				paintHandles2(g);
			}
		}
	}

	protected void paintHandles(Graphics g) {
		//check mouse position
		if(position==MOVE||position==RESIZE_NW||position==RESIZE_NE||position==RESIZE_SE||position==RESIZE_SW||position==RESIZE_N||position==RESIZE_E||position==RESIZE_S||position==RESIZE_W){
				
			// the handles on the outer circle
			
			//calculate the positions as pixel size
			int intX1 = (int)(OutX1*(width-1));
			int intY1 = (int)(OutY1*(height-1));
			int intRadius1 = (int)(RadiusOut*(width-1));
			
			//paint the white background for the corner handles
			g.setColor(Color.WHITE);
			g.fillRect(intX1+intRadius1-handlesize/2, intY1-handlesize/2, handlesize, handlesize);
			g.fillRect(intX1-intRadius1-handlesize/2, intY1-handlesize/2, handlesize, handlesize);
			g.fillRect(intX1-handlesize/2, intY1+intRadius1-handlesize/2, handlesize, handlesize);
			g.fillRect(intX1-handlesize/2, intY1-intRadius1-handlesize/2, handlesize, handlesize);
		
			//paint the black frame for the corner handles
			g.setColor(Color.BLACK);
			g.drawRect(intX1+intRadius1-handlesize/2, intY1-handlesize/2, handlesize, handlesize);
			g.drawRect(intX1-intRadius1-handlesize/2, intY1-handlesize/2, handlesize, handlesize);
			g.drawRect(intX1-handlesize/2, intY1+intRadius1-handlesize/2, handlesize, handlesize);
			g.drawRect(intX1-handlesize/2, intY1-intRadius1-handlesize/2, handlesize, handlesize);
		}
	}

	protected void paintHandles2(Graphics g) {
		//check mouse position
		if(showRadius2 && (position==MOVE||position==RESIZE_NW_2||position==RESIZE_NE_2||position==RESIZE_SE_2||position==RESIZE_SW_2||position==RESIZE_N_2||position==RESIZE_E_2||position==RESIZE_S_2||position==RESIZE_W_2)){	
			// the handles on the inner circle

			//calculate the positions as pixel size
			int intX1 = (int)(OutX1*(width-1));
			int intY1 = (int)(OutY1*(height-1));
			int intRadius2 = (int)(Radius2Out*(width-1));
			
			//paint the white background for the corner handles
			g.setColor(Color.WHITE);
			g.fillRect(intX1+intRadius2-handlesize/2, intY1-handlesize/2, handlesize, handlesize);
			g.fillRect(intX1-intRadius2-handlesize/2, intY1-handlesize/2, handlesize, handlesize);
			g.fillRect(intX1-handlesize/2, intY1+intRadius2-handlesize/2, handlesize, handlesize);
			g.fillRect(intX1-handlesize/2, intY1-intRadius2-handlesize/2, handlesize, handlesize);
		
			//paint the black frame for the corner handles
			g.setColor(Color.BLACK);
			g.drawRect(intX1+intRadius2-handlesize/2, intY1-handlesize/2, handlesize, handlesize);
			g.drawRect(intX1-intRadius2-handlesize/2, intY1-handlesize/2, handlesize, handlesize);
			g.drawRect(intX1-handlesize/2, intY1+intRadius2-handlesize/2, handlesize, handlesize);
			g.drawRect(intX1-handlesize/2, intY1-intRadius2-handlesize/2, handlesize, handlesize);
		}
	}

	protected int checkState(int xIn, int yIn) {
		//check close to which circle and side the mouse is and chance the state accordingly
		checkDimensions(false);

		//calculate the positions as pixel size
		int intX1 = (int)(OutX1*(width-1));
		int intY1 = (int)(OutY1*(height-1));
		int intRadius1 = (int)(RadiusOut*(width-1));
		int intRadius2 = (int)(Radius2Out*(width-1));
		
		double dist = (xIn-intX1)*(xIn-intX1)+(yIn-intY1)*(yIn-intY1);
		if(initial){
			//initially the user has to make the circle
			return MAKE;
		}else if(dist<(intRadius1+5)*(intRadius1+5) && dist>(intRadius1-5)*(intRadius1-5)){
			//check if the cursor is on the outer ring
			if(xIn<intX1-intRadius1+10 && xIn>intX1-intRadius1-10 && yIn<intY1+intRadius1/2 && yIn>intY1-intRadius1/2){
				return RESIZE_W;
			}else if(xIn<intX1+intRadius1+10 && xIn>intX1+intRadius1-10 && yIn<intY1+intRadius1/2 && yIn>intY1-intRadius1/2){
				return RESIZE_E;
			}else if(yIn<intY1-intRadius1+10 && yIn>intY1-intRadius1-10 && xIn<intX1+intRadius1/2 && xIn>intX1-intRadius1/2){
				return RESIZE_N;
			}else if(yIn<intY1+intRadius1+10 && yIn>intY1+intRadius1-10 && xIn<intX1+intRadius1/2 && xIn>intX1-intRadius1/2){
				return RESIZE_S;
			}else if(xIn<intX1 && yIn<intY1){
				return RESIZE_NW;
			}else if(xIn>=intX1 && yIn<intY1){
				return RESIZE_NE;
			}else if(xIn<intX1 && yIn>=intY1){
				return RESIZE_SW;
			}else{
				return RESIZE_SE;
			}
		}else if(dist<(intRadius2+5)*(intRadius2+5) && dist>(intRadius2-5)*(intRadius2-5) && showRadius2){
			//check if the cursor is on the inner ring, only if it doesn't overlap with the outer ring and the inner ring is visible
			if(xIn<intX1-intRadius2+10 && xIn>intX1-intRadius2-10 && yIn<intY1+intRadius2/2 && yIn>intY1-intRadius2/2){
				return RESIZE_W_2;
			}else if(xIn<intX1+intRadius2+10 && xIn>intX1+intRadius2-10 && yIn<intY1+intRadius2/2 && yIn>intY1-intRadius2/2){
				return RESIZE_E_2;
			}else if(yIn<intY1-intRadius2+10 && yIn>intY1-intRadius2-10 && xIn<intX1+intRadius2/2 && xIn>intX1-intRadius2/2){
				return RESIZE_N_2;
			}else if(yIn<intY1+intRadius2+10 && yIn>intY1+intRadius2-10 && xIn<intX1+intRadius2/2 && xIn>intX1-intRadius2/2){
				return RESIZE_S_2;
			}else if(xIn<intX1 && yIn<intY1){
				return RESIZE_NW_2;
			}else if(xIn>=intX1 && yIn<intY1){
				return RESIZE_NE_2;
			}else if(xIn<intX1 && yIn>=intY1){
				return RESIZE_SW_2;
			}else{
				return RESIZE_SE_2;
			}
		}else if(dist<(intRadius1-5)*(intRadius1-5)){
			//if it is in the outer ring and none of the other options the user can move it
			return MOVE;
		}else{
			//if the cursor is outside the circle
			return NOTHING;
		}
	}

	public void checkDimensions(boolean changeOriginal){
		//check if the variables are within the allowed range and adjust them to the current zoom
		double xTemp = X1;
		if(X1>1){
			xTemp = 1;
		}else if(X1<0){
			xTemp = 0;
		}		
		OutX1 = xTemp;

		double yTemp = Y1;
		if(Y1>1){
			yTemp = 1;
		}else if(Y1<0){
			yTemp = 0;
		}
		OutY1 = yTemp;
		
		double radiusTemp = Radius;
		if(radiusTemp>1 && radiusTemp>height/(double)(width-1)){
			radiusTemp = Math.max(1, height/(double)(width-1));
		}
		RadiusOut =radiusTemp;
		
		double radius2Temp = Radius2;
		if(radius2Temp>=Radius){
			radius2Temp = Radius-1/(double)(width-1);
		}
		Radius2Out =radius2Temp;
		
		//is called when the mouse is released. In order to ensure smooth drag and drop for resizing the (possibly wrong positioned) coordinates are needed
		if(changeOriginal){
			X1=xTemp;
			Y1=yTemp;
			Radius=radiusTemp;
			Radius2=radius2Temp;
		}
	}
	protected void changeMousePosition(MouseEvent arg0) {
		//a method that is called by dragMouse and releaseMouse to draw a current image of the box if it is moved or resized
		
		//calculate the mouse input for the zoomed image
		double inputX = arg0.getX()/(double)(width-1);
		double inputY = arg0.getY()/(double)(height-1);
		
		if(state==RESIZE_NW || state==RESIZE_SW || state==RESIZE_NE || state==RESIZE_SE || state==RESIZE_N || state==RESIZE_S || state==RESIZE_E || state==RESIZE_W){
			//the user resizes the outer circle
			Radius = Math.sqrt((inputX-X1)*(inputX-X1)+(inputY-Y1)*(inputY-Y1));
			
		}else if(state==RESIZE_NW_2 || state==RESIZE_SW_2 || state==RESIZE_NE_2 || state==RESIZE_SE_2 || state==RESIZE_N_2 || state==RESIZE_S_2 || state==RESIZE_E_2 || state==RESIZE_W_2){
			//the user resizes the inner circle
			Radius2 = Math.sqrt((inputX-X1)*(inputX-X1)+(inputY-Y1)*(inputY-Y1));
			
		}else{
			super.changeMousePosition(arg0);
		}
	}

	protected void setMouseCursor(MouseEvent arg0) {
		//set the mouse cursor according to the current position 
		position = checkState((int)(arg0.getX()),(int)(arg0.getY()));
		Cursor oldCursor = parentPanel.getCursor();
		if(position==MAKE){
			parentPanel.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
		}else if(position==MOVE){
			parentPanel.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
		}else if(position==RESIZE_NW || position==RESIZE_NW_2){
			parentPanel.setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR));
		}else if(position==RESIZE_NE || position==RESIZE_NE_2){
			parentPanel.setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR));
		}else if(position==RESIZE_SE || position==RESIZE_SE_2){
			parentPanel.setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
		}else if(position==RESIZE_SW || position==RESIZE_SW_2){
			parentPanel.setCursor(Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR));
		}else if(position==RESIZE_N || position==RESIZE_N_2){
			parentPanel.setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
		}else if(position==RESIZE_E || position==RESIZE_E_2){
			parentPanel.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
		}else if(position==RESIZE_S || position==RESIZE_S_2){
			parentPanel.setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
		}else if(position==RESIZE_W || position==RESIZE_W_2){
			parentPanel.setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
		}else{
			parentPanel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		}
		//only repaint if the cursor has changed
		if(oldCursor!=parentPanel.getCursor()){
			parentPanel.renderPreview();
		}
	}

	public void updateParent(){
		parentPanel.updateController(OutX1,OutY1,RadiusOut,Radius2Out);
	}
	
	public void update(double x,double y,double radius, double radius2){
		this.X1 = x;
		this.Y1 =y;
		this.Radius = radius;
		this.Radius2 = radius2;
		checkDimensions(true);
	}

	public void keyPressed(KeyEvent arg0) {
		
		//enlarge the outer circle
		if(arg0.getKeyCode()==KeyEvent.VK_PLUS){
			Radius=Radius+1/(double)(width-1);
			updateParent();
		}
		//shrink the outer circle
		else if(arg0.getKeyCode()==KeyEvent.VK_MINUS){
			Radius=Radius-1/(double)(width-1);
			updateParent();
		}else{
			super.keyPressed(arg0);
		}
	}
	
	public double getRadius() {
		return Radius;
	}

	public void setRadius(double radius) {
		Radius = radius;
	}

	public double getRadius2() {
		return Radius2;
	}

	public void setRadius2(double radius2) {
		Radius2 = radius2;
	}

	public double getRadiusOut() {
		return RadiusOut;
	}

	public void setRadiusOut(double radiusOut) {
		RadiusOut = radiusOut;
	}

	public double getRadius2Out() {
		return Radius2Out;
	}

	public void setRadius2Out(double radius2Out) {
		Radius2Out = radius2Out;
	}

	public boolean isShowRadius2() {
		return showRadius2;
	}

	public void setShowRadius2(boolean showRadius2) {
		this.showRadius2 = showRadius2;
	}

	public double getObjectWidth() {
		return RadiusOut * 2;
	}

	public double getObjectHeight() {
		//the radius is always stored as a double value in relation to width, but the caller of this method expects 
		//the double value for the height in relation to the height
		return (RadiusOut * 2)/(width-1)*(height-1);
	}

	public int getCrossSize() {
		return crossSize;
	}

	public void setCrossSize(int crossSize) {
		this.crossSize = crossSize;
	}
}